package com.fcat.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.fcat.gateway.model.Result;
import com.fcat.gateway.model.StateCode;
import com.fcat.springboot.redis.service.RedisService;
import com.redis.common.TokenInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    private final Logger logger = LoggerFactory.getLogger(AuthorizeFilter.class);

    public static final String AUTHORIZATION_HEADER = "Authorization";
    private final String userTokenMapInRedis = "token:userTokenMap";
    public static final String  tokenUserMap = "token:tokenUserMap";

    @Value("${accessTokenTimeM}")
    private Long accessTokenTimeM;

    @Autowired
    private RedisService redisService;

    //放行的路径
    private static List<String> whiteList = new ArrayList<>();

    static {
        whiteList.add("/common-user/auth/login");
        whiteList.add("/project-main/user/registerUser");
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest httpRequest = exchange.getRequest();

        //获取url
        String url = httpRequest.getURI().getPath();

        logger.info("访问：" + url);

        //判断url是否直接放行
        if (whiteList.contains(url)) {
            return chain.filter(exchange);
        }

        List<String> accessTokenList = httpRequest.getHeaders().get(AUTHORIZATION_HEADER);
        String accessToken = null;
        if (CollectionUtils.isEmpty(accessTokenList)) {
            return getResponseContext(StateCode.NO_TOKEN, exchange);
        } else {
            accessToken = accessTokenList.get(0);
        }
        logger.debug("accessToken：" + accessToken);

        TokenInfo tokenInfo = (TokenInfo) redisService.mapGet(tokenUserMap,accessToken);
        String username = null;
        if(tokenInfo==null || tokenInfo.getUsername()==null){
            return getResponseContext(StateCode.INVALID_TOKEN, exchange);
        }else{
            username = tokenInfo.getUsername();
        }


        if (!username.equals("super")) {
            username= username.toLowerCase();
            Set<String> tokenInfos = (Set<String>) redisService.mapGet(userTokenMapInRedis, username);

            //token超时
            if( System.currentTimeMillis() - tokenInfo.getLastTime().getTime() > accessTokenTimeM * 60 * 1000){
                redisService.mapDelete(tokenUserMap,accessToken);
                tokenInfos.remove(accessToken);
                redisService.mapPut(userTokenMapInRedis, username, tokenInfos);
                return getResponseContext(StateCode.INVALID_TOKEN, exchange);
            }
             //更新时间
            tokenInfo.setLastTime(new Date());
            redisService.mapPut(userTokenMapInRedis, username, tokenInfos);
        }


        return chain.filter(exchange);
    }

    private void removeAccessToken(String accessToken, Set<String> tokenInfos) {
        Iterator<String> tokenIterator = tokenInfos.iterator();
        while (tokenIterator.hasNext()) {
            String token = tokenIterator.next();
            if (token.equals(accessToken)) {
                tokenIterator.remove();
            }
        }
    }
    private void removeOtherAccessToken(String accessToken, Set<String> tokenInfos) {
        Iterator<String> tokeIterator = tokenInfos.iterator();
        while (tokeIterator.hasNext()) {
            String token = tokeIterator.next();
            if (!token.equals(accessToken)) {
                tokeIterator.remove();
            }
        }
    }

    Mono<Void> getResponseContext(StateCode stateCode, ServerWebExchange exchange) {

        Result result = null;
        switch (stateCode) {
            case HEADER_NOUSERNAME:
                logger.warn("request header 'username' is not present");
                result = Result.failure(StateCode.HEADER_NOUSERNAME);
                break;
            case NO_TOKEN:
                logger.warn("Authorization token is empty");
                result = Result.failure(StateCode.NO_TOKEN);
                break;
            case INVALID_TOKEN:
                logger.warn("token is timeout，Authorization token is invalid");
                result = Result.failure(StateCode.INVALID_TOKEN);
                break;
            case INVALID_TOKEN3:
                logger.warn("token is invalid, crowded");
                result = Result.failure(StateCode.INVALID_TOKEN3);
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + stateCode.getCode());
        }
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        DataBuffer d = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        //指定编码
        serverHttpResponse.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return serverHttpResponse.writeWith(Flux.just(d));
    }

    //按级别进行
    @Override
    public int getOrder() {
        return 0;
    }
}
